package esayexcel.demo;

import com.alibaba.excel.metadata.Head;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.write.handler.CellWriteHandler;
import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
import com.alibaba.excel.write.metadata.holder.WriteTableHolder;

import java.util.ArrayList;
import java.util.List;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellType;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.util.CellRangeAddress;


public class DynamicHeaderHandler implements CellWriteHandler {
    //横向合并
    private int[] mergeRowIndexRange;
    private int mergeColumnIndexStart;
    private int mergeColumnIndexEnd;
    //纵向合并
    private int[] mergeColumnIndexRange;
    private int mergeRowIndexStart;
    private int mergeRowIndexEnd;

    public DynamicHeaderHandler() {
    }

    /**
     * 纵向合并入参
     *
     * @param mergeRowIndexStart 开始合并的行索引
     * @param mergeRowIndexEnd   结束合并的行索引
     * @param mergeColumnIndex   要合并的列索引数组
     */
    public DynamicHeaderHandler(int mergeRowIndexStart, int mergeRowIndexEnd, int[] mergeColumnIndex) {
        this.mergeRowIndexStart = mergeRowIndexStart;
        this.mergeRowIndexEnd = mergeRowIndexEnd;
        this.mergeColumnIndexRange = mergeColumnIndex;
    }


    /**
     * 横向合并入参
     *
     * @param mergeRowIndexStart    开始合并的行索引
     * @param mergeRowIndexEnd      结束合并的行索引
     * @param mergeColumnIndexStart 开始合并的列索引
     * @param mergeColumnIndexEnd   结束合并的列索引
     */
    public DynamicHeaderHandler(int mergeRowIndexStart, int mergeRowIndexEnd, int mergeColumnIndexStart, int mergeColumnIndexEnd) {
        this.mergeRowIndexRange = new int[]{mergeRowIndexStart, mergeRowIndexEnd};
        this.mergeColumnIndexStart = mergeColumnIndexStart;
        this.mergeColumnIndexEnd = mergeColumnIndexEnd;
    }

    @Override
    public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
        // 当前行索引
        int curRowIndex = cell.getRowIndex();
        // 当前列索引
        int curColIndex = cell.getColumnIndex();
        //横向合并
        if (mergeRowIndexRange != null && curRowIndex >= mergeRowIndexRange[0] && curRowIndex <= mergeRowIndexRange[1]) {
            if (curColIndex >= mergeColumnIndexStart && curColIndex <= mergeColumnIndexEnd) {
                mergeHorizontally(writeSheetHolder, cell, curRowIndex, curColIndex);
            }
        }
        //纵向合并
        if (mergeColumnIndexRange != null && mergeRowIndexStart != -1 && curRowIndex >= mergeRowIndexStart && curRowIndex <= mergeRowIndexEnd) {
            for (int columnIndex : mergeColumnIndexRange) {
                if (curColIndex == columnIndex) {
                    mergeWithPrevRow(writeSheetHolder, cell, curRowIndex, curColIndex);
                    break;
                }
            }
        }

    }

    /**
     * 当前单元格纵向合并
     *
     * @param writeSheetHolder 当前工作表持有者
     * @param cell             当前单元格
     * @param curRowIndex      当前行索引
     * @param curColIndex      当前列索引
     */
    private void mergeWithPrevRow(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        // 获取当前行的当前列的数据和上一行的当前列列数据，通过上一行数据是否相同进行合并
        Object curData = cell.getCellTypeEnum() == CellType.STRING ? cell.getStringCellValue() : cell.getNumericCellValue();
        // 获取前一个单元格的数据
        Cell preCell = cell.getSheet().getRow(curRowIndex - 1).getCell(curColIndex);
        if (preCell != null) {
            Object preData = preCell.getCellTypeEnum() == CellType.STRING ? preCell.getStringCellValue() : preCell.getNumericCellValue();
            if (curData.equals(preData)) {
                // 获取工作表
                Sheet sheet = writeSheetHolder.getSheet();
                // 获取已合并的区域
                List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
                boolean isMerged = false;
                // 检查前一个单元格是否已经被合并
                // 移除与新合并区域可能重叠的所有现有合并区域
                for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
                    CellRangeAddress cellRangeAddr = mergeRegions.get(i);
                    // 若上一个单元格已经被合并，则先移出原有的合并单元，再重新添加合并单元
                    if (cellRangeAddr.isInRange(curRowIndex - 1, curColIndex)) {
                        sheet.removeMergedRegion(i);
                        cellRangeAddr.setLastRow(curRowIndex);
                        sheet.addMergedRegion(cellRangeAddr);
                        isMerged = true;
                    }

                }
                // 如果前一个单元格未被合并，则新增合并区域
                if (!isMerged) {
                    CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex - 1, curRowIndex, curColIndex, curColIndex);
                    sheet.addMergedRegion(cellRangeAddress);
                }
            }
        }
    }


    private void mergeHorizontally(WriteSheetHolder writeSheetHolder, Cell cell, int curRowIndex, int curColIndex) {
        Sheet sheet = writeSheetHolder.getSheet();
        // 移除与新合并区域可能重叠的所有现有合并区域
        removeOverlappingMerges(sheet, new CellRangeAddress(curRowIndex, curRowIndex, mergeColumnIndexStart, mergeColumnIndexEnd));
        List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
        boolean isMerged = false;

        for (int i = 0; i < mergeRegions.size() && !isMerged; i++) {
            CellRangeAddress cellRangeAddr = mergeRegions.get(i);
            if (cellRangeAddr.isInRange(curRowIndex, curColIndex)) {
                sheet.removeMergedRegion(i);
                cellRangeAddr.setLastColumn(curColIndex);
                sheet.addMergedRegion(cellRangeAddr);
                isMerged = true;
            }
        }

        if (!isMerged) {
            CellRangeAddress cellRangeAddress = new CellRangeAddress(curRowIndex, curRowIndex, mergeColumnIndexStart, mergeColumnIndexEnd);
            sheet.addMergedRegion(cellRangeAddress);
        }
    }

    private void removeOverlappingMerges(Sheet sheet, CellRangeAddress newMergeArea) {
        List<CellRangeAddress> mergeRegions = sheet.getMergedRegions();
        List<Integer> regionsToRemove = new ArrayList<>();

        for (int i = 0; i < mergeRegions.size(); i++) {
            CellRangeAddress existingMerge = mergeRegions.get(i);
            if (isOverlapping(newMergeArea, existingMerge)) {
                regionsToRemove.add(i);
            }
        }

        for (int i = regionsToRemove.size() - 1; i >= 0; i--) {
            sheet.removeMergedRegion(regionsToRemove.get(i));
        }
    }

    /**
     * 判断两个合并区域是否重叠
     */
    private boolean isOverlapping(CellRangeAddress area1, CellRangeAddress area2) {
        return !(area1.getFirstRow() > area2.getLastRow() ||
                area1.getLastRow() < area2.getFirstRow() ||
                area1.getFirstColumn() > area2.getLastColumn() ||
                area1.getLastColumn() < area2.getFirstColumn());
    }

    @Override
    public void beforeCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Row row, Head head, Integer integer, Integer integer1, Boolean aBoolean) {

    }

    @Override
    public void afterCellCreate(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, Cell cell, Head head, Integer integer, Boolean aBoolean) {

    }
}